import time
from marshmallow import fields

from class_broker.apps.api_ngx.services import UserService
from webutils.compat import complex_json
from webutils.datetime import strftime
from webutils.tornado.responses import Response
from webutils.tornado.web import RequestHandler
from webutils.webargs.tornado import use_args
from webutils.webargs.bare import parser
from webutils.random import random_string
from webutils.datastructure import NameSpace

from message_broker.services import RoomService
from pubsub.brokers.rabbitmq import RabbitMQ

from .. import params, tasks
from .. import broadcaster
from ..const import UserGroup


class StateHandler(RequestHandler):
    @use_args(
        {
            'token': params.Token(required=True),
            'module_id': fields.Str(required=False, missing=None),
        },
        location='query')
    async def get(self, args, room_id=None):
        service = RoomService(self.connection, room_id=room_id)
        dct = await service.get_state()
        lst = []
        for key, value in dct.items():
            item = {
                'mod': key,
                'msg': value['msg'],
                'updated_at': strftime(value['updated_at']),
            }
            lst.append(item)
        data = {'total': len(lst), 'list': lst}
        resp = Response(errcode=0, errmsg='ok', data=data)
        self.write_json(resp)


class StateHistoryHandler(RequestHandler):
    @use_args(
        {
            'token': params.Token(required=True),
            'module_id': fields.Str(required=False, missing=None),
        },
        location='query')
    async def get(self, args, room_id=None):
        service = RoomService(self.connection, room_id=room_id)
        if args['module_id']:
            lst = await service.get_state_history(args['module_id'])
            dct = {args['module_id']: lst}
        else:
            dct = await service.get_all_state_history()
        all_state = []
        for key, origin_history_lst in dct.items():
            if not origin_history_lst:
                continue
            history_lst = []
            for history in origin_history_lst:
                item = {
                    'mod': key,
                    'msg': history['msg'],
                    'updated_at': strftime(history['updated_at']),
                }
                history_lst.append(item)
            all_state.append(history_lst)
        data = {'total': len(all_state), 'list': all_state}
        resp = Response(errcode=0, errmsg='ok', data=data)
        self.write_json(resp)


class StateAddHandler(RequestHandler):
    @use_args(
        {
            'token': params.Token(required=True),
            'unit_id': fields.Str(required=True),
        },
        location='query')
    async def post(self, origin_args):
        try:
            body = complex_json.loads(self.request.body)
        except complex_json.decoder.JSONDecodeError:
            msg = 'json bady not loads'
            resp = Response(errcode=1, errmsg=msg)
            return self.write_json(resp)
        room_id = origin_args['unit_id']
        token = origin_args['token']
        room_service = RoomService(self.connection, room_id=room_id)
        user_service = UserService(self.connection, room_id=room_id)
        session_type, user = await user_service.get_user_info(token)
        if not user:
            msg = 'The access token provided is invalid'
            resp = Response(errcode=1, errmsg=msg)
            return self.write_json(resp)
        elif user == 'device_error':
            msg = 'The device token not device_type or classroom_id info'
            resp = Response(errcode=1, errmsg=msg)
            return self.write_json(resp)
        self.EXCHANGE_NAME = 'base_broker'
        self.session_id = user.id
        broadcaster_id = '_'.join([
            room_id,
            self.session_id,
            random_string(length=4),
        ])
        broker = RabbitMQ(
            self.application.settings['RABBITMQ'],
            broadcaster_id,
            self.log_context,
            exchange_name=self.EXCHANGE_NAME,
        )
        channels_subscribed = {
            'session_id': self.session_id,
            'x-match': 'any',
            'group': UserGroup.ALL.value,
            'room_id': room_id,
        }
        task_meta = {
            'session_id': self.session_id,
        }
        if user:
            channels_subscribed['user_id'] = user.id
            task_meta['session_user_id'] = user.id

        self.broadcaster_app = broadcaster.BrokerApp(
            broker,
            room_id=room_id,
            tasks=tasks.tasks,
            task_meta=task_meta,
            channels_subscribed=channels_subscribed,
        )

        await self.broadcaster_app.start_consume(self)
        self.broadcaster_app.websocket = NameSpace()
        self.broadcaster_app.websocket.is_live = 1
        schema = {
            'type': fields.Str(),
            'mod': fields.Str(required=True),
            'to': params.Receiver(required=True),
            'from': params.Sender(required=False),
            'msg': fields.Dict(required=True),
        }
        args = parser.parse(schema, body)
        body['updated_at'] = time.time()
        body['created_at'] = time.time()
        await room_service.update_state(body)
        for group in args['to']['groups']:
            await self.broadcaster_app.send_task(
                'broadcast_state',
                body,
                force=True,
                group=group,
            )
        for user_id in args['to']['users']:
            await self.broadcaster_app.send_task(
                'broadcast_state',
                body,
                force=True,
                user_id=user_id,
            )
        msg = {
            "errcode": 0,
            "errmsg": "ok"
        }
        return self.write_json(msg)
